#include "binary2base64.h"

typedef unsigned char uchar;

binary2Base64::binary2Base64()
{

}

std::string binary2Base64::toBase64(const unsigned char* pSource,
                     int iSourceCount, zqyBase64Option options)
{
    const char alphabet_base64[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
                                   "ghijklmn" "opqrstuv" "wxyz0123" "456789+/";
    const char alphabet_base64url[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
                                      "ghijklmn" "opqrstuv" "wxyz0123" "456789-_";
    const char *const alphabet = options & eBase64UrlEncoding ? alphabet_base64url : alphabet_base64;
    const char padchar = '=';
    int padlen = 0;

	if (!iSourceCount)
	{
		return "";
	}

    unsigned char* tmp = (unsigned char*)malloc((iSourceCount + 2) / 3 * 4);
	if (!tmp)
	{
		return nullptr;
	}

    int i = 0;
    unsigned char* out = &tmp[0];
    while (i < iSourceCount) {
        // encode 3 bytes at a time
        int chunk = 0;
        chunk |= int(uchar(pSource[i++])) << 16;
        if (i == iSourceCount) {
            padlen = 2;
        } else {
            chunk |= int(uchar(pSource[i++])) << 8;
            if (i == iSourceCount)
                padlen = 1;
            else
                chunk |= int(uchar(pSource[i++]));
        }

        int j = (chunk & 0x00fc0000) >> 18;
        int k = (chunk & 0x0003f000) >> 12;
        int l = (chunk & 0x00000fc0) >> 6;
        int m = (chunk & 0x0000003f);
        *out++ = alphabet[j];
        *out++ = alphabet[k];

        if (padlen > 1) {
            if ((options & eOmitTrailingEquals) == 0)
                *out++ = padchar;
        } else {
            *out++ = alphabet[l];
        }
        if (padlen > 0) {
            if ((options & eOmitTrailingEquals) == 0)
                *out++ = padchar;
        } else {
            *out++ = alphabet[m];
        }
    }
//    Q_ASSERT((options & eOmitTrailingEquals) || (out == tmp.size() + tmp.data()));
//    if (options & eOmitTrailingEquals)
//        tmp.resize(out - tmp.data());
    std::string ret(tmp, out);
    free(tmp);
    return ret;
}

unsigned char *binary2Base64::fromBase64(const std::string &base64, zqyBase64Option options, int &iSize)
{
    if(base64.empty())
    {
        return nullptr;
    }
    unsigned int buf = 0;
    int nbits = 0;
    unsigned char* tmp = (unsigned char*)malloc((base64.size() * 3) / 4);
	if (!tmp)
	{
		return nullptr;
	}

    int offset = 0;
    for (int i = 0; i < base64.size(); ++i) {
        int ch = base64.at(i);
        int d;

        if (ch >= 'A' && ch <= 'Z')
            d = ch - 'A';
        else if (ch >= 'a' && ch <= 'z')
            d = ch - 'a' + 26;
        else if (ch >= '0' && ch <= '9')
            d = ch - '0' + 52;
        else if (ch == '+' && (options & eBase64UrlEncoding) == 0)
            d = 62;
        else if (ch == '-' && (options & eBase64UrlEncoding) != 0)
            d = 62;
        else if (ch == '/' && (options & eBase64UrlEncoding) == 0)
            d = 63;
        else if (ch == '_' && (options & eBase64UrlEncoding) != 0)
            d = 63;
        else
            d = -1;

        if (d != -1) {
            buf = (buf << 6) | d;
            nbits += 6;
            if (nbits >= 8) {
                nbits -= 8;
                tmp[offset++] = buf >> nbits;
                buf &= (1 << nbits) - 1;
            }
        }
    }

    unsigned char* pRe = (unsigned char*)realloc(tmp, offset);
	if (!pRe)
	{
		free(tmp);
		return nullptr;
	}

    iSize = offset;
    return pRe;
}
